#ifndef TIME_MGMT

#define TIME_MGMT
#include <vector>
#include <Windows.h>

void test();

inline VOID timerFct_default(VOID)
{
    //default behavior not determined: Dummy
}

class timers {
public:
    static const enum timersEnum { 
                                  IDT_UNDEF=0, IDT_FTIMER = 10, 
                                  IDT_TIMER1 = IDT_FTIMER, IDT_TIMER2, IDT_TIMER3, IDT_TIMER4, IDT_TIMER5, 
                                  IDT_LTIMER, IDT_TIMER_SIZE = IDT_LTIMER-IDT_FTIMER,  
                                 };
    static const enum timerKind  {
                                  IDT_KUNDEF=0, 
                                  IDT_KTIMER, 
                                  IDT_KTIMEOUT
                                 };
    static timers &instance() { static timers tinstance; return tinstance; }

    UINT storeTimerIdEvent(timersEnum timer, UINT TimerIDEvent) { 
        UINT timPos = timer-IDT_FTIMER;
        if(timersData[timPos].taken == TRUE) {
            timersData[timPos].TimerID=TimerIDEvent;
            return EXIT_SUCCESS;   
        } 
        
        return EXIT_FAILURE;
    }

    VOID (*getTimerDefFct(timersEnum timer))(VOID) { 
        UINT timPos = timer-IDT_FTIMER;
        if( (timersData[timPos].taken == TRUE) && (timersData[timPos].fct != NULL) ) 
             return timersData[timPos].fct;   

        return timerFct_default;
    }

    timersEnum getTimerNo(UINT TimerIDEvent) const {
        for(UINT i=0, k=timersData.size(); i<k; ++i)
            if(timersData[i].taken == TRUE && timersData[i].TimerID == TimerIDEvent)
                return timersEnum(IDT_FTIMER+i);
        return IDT_UNDEF;
    }

    UINT getTimerMsInterval(timersEnum timer) const { 
        UINT timPos = timer-IDT_FTIMER;
        if(timersData[timPos].taken == TRUE) 
            return timersData[timPos].ms_interval;   
        
        return 0;
    }
    VOID updatePeekTimeouts(){      
         for(UINT tim=0; tim<IDT_TIMER_SIZE; ++tim ) {
            if( (timersData[tim].taken == TRUE) && (timersData[tim].Type == IDT_KTIMEOUT) ) {
                if(timersData[tim].TimeoutDelayMs > 0)
                    timersData[tim].TimeoutDelayMs -= 1;                     
            }
        }
    }
    UINT createTimer(const UINT ms_interval, void (*ext_funct)(void) );
    UINT createTimeout(const UINT ms_interval) { Sleep(ms_interval); };
    UINT createPeekTimeout(const UINT ms_delay);
    UINT peekTimeout(const UINT timeoutID) const { 
        return timersData[timeoutID].TimeoutDelayMs; 
    };

private:
    timers():timersData(IDT_TIMER_SIZE, tim_info()) {}
    timers(const timers&);
    timers &operator=(const timers&);

    

    struct tim_info{
        tim_info():taken(FALSE), TimerID(0), Type(IDT_KUNDEF), TimeoutDelayMs(0), ThreadID(0), hThread(0), fct(NULL), ms_interval(0) {}
        tim_info(timerKind tKind, UINT Timeout, DWORD tID, HANDLE thandle, VOID (*f)(void), UINT ms_ival):
                taken(TRUE), TimerID(0), Type(tKind), TimeoutDelayMs(Timeout), ThreadID(tID), hThread(thandle), fct(f), ms_interval(ms_ival) {}

        BOOL      taken;
        UINT      TimerID;
        timerKind Type;
        UINT      TimeoutDelayMs;
        DWORD     ThreadID;
        HANDLE    hThread;
        VOID      (*fct)(void);
        UINT      ms_interval;
    };
    std::vector<tim_info> timersData;
    
    static HANDLE evMutex; 

    BOOL allTaken() const { if(getFreeTimer() == IDT_UNDEF) TRUE; return FALSE; }
    timersEnum getFreeTimer() const {
         for(UINT tim=0; tim<IDT_TIMER_SIZE; ++tim ) 
            if( timersData[tim].taken == FALSE ) 
                return timersEnum(IDT_FTIMER+tim); 
         return IDT_UNDEF; 
    }
    UINT setTaken(timersEnum newTaken, timerKind Kind, UINT Timeout, DWORD ThreadID, HANDLE hThread, VOID (*fct)(void), UINT ms_interval){      
        
        UINT timPos = newTaken-IDT_FTIMER;
        if( timPos >= 0 && timersData[timPos].taken == FALSE ) {
            timersData[timPos] = tim_info(Kind, Timeout, ThreadID, hThread, fct, ms_interval);
            return TRUE;
        }
        else
            return FALSE; 
    }
    UINT releaseTaken(timersEnum newTaken){      
        
        UINT timPos = newTaken-IDT_FTIMER;
        if( timPos >= 0 && timersData[timPos].taken == TRUE ) {
            timersData[timPos] = tim_info();
            return TRUE;
        }
        else
            return FALSE;
    }
    BOOL isTimeoutDeployed(){      
         for(UINT tim=0; tim<IDT_TIMER_SIZE; ++tim ) 
            if( (timersData[tim].taken == TRUE) && (timersData[tim].Type == IDT_KTIMEOUT) ) 
                return TRUE; 
         return FALSE;
    }

};

#endif